from functools import wraps
from socket import close
import asyncio.subprocess as sub
import subprocess
from types import coroutine
from sanic import Request, Sanic, Websocket, response
import asyncio
from threading import Thread
import json
import nest_asyncio
from time import sleep
from sanic.response import text
from sanic.response import json
from sanic.views import HTTPMethodView
from sanic_jinja2 import SanicJinja2

from sanic.server.websockets.impl import WebsocketImplProtocol
from sanic.signals import Event
from sanic.log import logger

nest_asyncio.apply()

app = Sanic(__name__)


class CListen(Thread):
    """Docstring for MyThread. """

    def __init__(self, loop):
        """TODO: to be defined.

        :*args: TODO
        :**kargs: TODO

        """
        Thread.__init__(self)
        self.mLoop = loop

    def run(self):
        """TODO: Docstring for run.
        :returns: TODO

        """
        asyncio.set_event_loop(self.mLoop)  # 在新线程中开启一个事件循环
        self.mLoop.run_forever()


async def callLater(proc, ws):
    while True:
        line = proc.stdout.readline()
        if not line:
            break
        await ws.send("abd" + line.decode("utf-8") + "sssssssss")
        await asyncio.sleep(0)


def start_loop(thread_loop):
    #  运行事件循环， loop以参数的形式传递进来运行
    asyncio.set_event_loop(thread_loop)
    thread_loop.run_forever()


@app.websocket('/ws')
async def feed(request, ws: WebsocketImplProtocol):
    # while True:
    #     receive_data = await ws.recv()
    #     print('服务端接收客户端: ' + receive_data)
    #
    #     send_data = f'hello! {receive_data}'
    #     print('服务端发给客户端: ' + send_data)
    #     await ws.send(send_data)
    try:
        receive_data = await ws.recv()
    except Exception as e:
        pass

    newLoop = asyncio.new_event_loop()
    t = Thread(target=start_loop, args=(newLoop, ))
    t.start()
    print("{}:".format(receive_data))
    proc = subprocess.Popen(receive_data.split(" "), stdout=subprocess.PIPE)
    # asyncio.run_coroutine_threadsafe(callLater(proc, ws), newLoop)
    # 将生产任务的协程注册到这个循环中
    advocate_loop = asyncio.get_event_loop()
    # 运行次循环
    advocate_loop.run_until_complete(callLater(proc, ws))


@app.websocket('/wxlink')
async def wxlink(request, ws: WebsocketImplProtocol):
    """docstring for wxlink"""
    print("来连接了")
    await ws.send("收到服务器的连接了")
    # while True:
    print("wait recv......")
    while True:
        data = await ws.recv()
        if data == "quit":
            print("ws close")
            close(ws)
        print(data)
        n = 0
        while True:
            if n > 300:
                await ws.send("stop")
                break
            await ws.send("ws.{}:{}".format(ws.ws_proto.id, data.upper()))
            await asyncio.sleep(0.1)
            n = n + 1


queue = {}
# gval = 0


@app.websocket('/shell')
async def shell(request, ws: WebsocketImplProtocol):
    """docstring for shell"""
    global queue
    global gval
    print("来连接了")
    # await ws.send(str(ws.__hash__()))
    await ws.send("收到服务器的连接了")
    print(ws.__hash__())
    queue[str(ws.__hash__())] = {"run": False, "terminal_flg": 0}  # 初始化中断标志
    print("发送ws_id")
    await ws.send(str(ws.__hash__()))
    # while True:
    print("wait recv......")
    print(queue)
    try:
        while True:
            cmd = await ws.recv()
            if cmd == "quit":
                print("ws close")
                close(ws)
                break
            print("收到执行命令:{}".format(cmd))
            cmd_list = []
            if cmd:
                cmd_list = cmd.split(" ")
            print(f"cmd_list: {cmd_list}")
            p = await sub.create_subprocess_exec(*cmd_list,
                                                  stdout=sub.PIPE,
                                                  stderr=sub.PIPE)
            queue[str(ws.__hash__())]["run"] = True
            while True:
                print("现在的ws_id:{}".format(queue.get(str(ws.__hash__()))))
                if queue.get(str(
                        ws.__hash__())).get("terminal_flg") == 1:  # 中断标志
                    await ws.send("查询被中断")
                    p.terminate()
                    queue[str(ws.__hash__())]["terminal_flg"] = 0  # reset中断标志
                    break
                line = await p.stdout.read(128)
                print(str(line.decode("utf-8")))
                try:
                    await ws.send(line.decode("utf-8"))
                except Exception as e:
                    print(e)
                if line == b'':
                    # await ws.send("stop".encode("utf-8"))
                    print("line 为空")
                    queue[str(ws.__hash__())]["terminal_flg"] = 0
                    break

            queue[str(ws.__hash__())]["run"] = False  # 不运行了

    except Exception as e:
        queue.pop(ws)
        print(e)
    finally:
        queue.pop(str(ws.__hash__()))


app.static("/static", "./static")
tp = SanicJinja2(app)


@app.route("/test.html")
async def test(request):
    return tp.render('test.html', request, data="xxxx")


@app.route("/stop")
async def stop_req(request):
    # global gval
    # gval = 1
    ws_id = request.args.get("ws_id")
    print("ws_id:{}".format(ws_id))
    if queue[ws_id]["run"]:
        queue[ws_id]["terminal_flg"] = 1
    else:
        return json({"ret": "没有运行如何服务"})
    return json({"ret": "ok"})


@app.route("/show_queue")
async def show_queue(request):
    print(queue)
    return json({"ret": queue})

async def feed01(request:Request, ws:Websocket):
    """docstring for feed"""
    await ws.send("tttttttt01")

app.add_websocket_route(feed01, "/feed01")

def xiushifu(msg):
    def log(f):
        """
        修饰符
        """
        def call(*args, **kargs):
            logger.info(f"{msg}")
            return f(*args, **kargs)
        return call
    return log


class MyView(HTTPMethodView):
    @xiushifu("hello world.................")
    async def get(self, request:Request, name):
        # logger.info("MyView")
        username = request.args.get("username")
        passwd = request.args.get("passwd")
        if username == "xiaogebin" and passwd == "123456":
            return json({"ret": "登录成功"}, 200)
        return text("Hello {}".format(name))

    async def post(self, request, name):
        return json({"ret": name}, 403)

app.add_route(MyView.as_view(), "/myview/<name>")


@app.get("/myget/<name>")
async def myget(request, name):
    return text("hello world: {}".format(name))


@app.signal(Event.HTTP_LIFECYCLE_COMPLETE)
async def my_signal_handle(conn_info):
    """docstring for my_signal_handle"""
    print("conn_info:{}".format(conn_info.sockname))
    print("Connection has been closed!!")


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=10000, debug=True, auto_reload=True, access_log=True)
